﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SosaAnalysisModule.Data;
using System.IO;

namespace SosaAnalysisModule
{
    public class AnalysisModule
    {
        /// <summary>
        /// This methods takes a filepath and a SosaData object
        /// This methods writes a csv file to the filepath by analsying the attributes of the SosaData object
        /// There are no returned values but a csv file will be written to the long-term memory
        /// </summary>
        /// <param name="filepath"></param>
        /// <param name="data"></param>
        public static bool CreateSingleAnalysisFile(SosaData data)
        {
            if (data == null) 
                return false;
            //Initilize Variables
            int numStimuli = data.PresentationOrder.Length;
            int numRow = 14 + numStimuli;
            int numCol = 4;
            int currentRow = 0;

            string[][] stringData = new string[numRow][];
            for (int i = 0; i < stringData.Length; i++)
                stringData[i] = new string[numCol];
            
            //Experiment Id
            stringData[currentRow][0] = "Experiment Id";
            stringData[currentRow++][1] = data.ExperimentID;
            currentRow++;
            //Time Data
            stringData[currentRow++][0] = "Time Data";
            stringData[currentRow][0] = "Time from preview to experiment start";
            stringData[currentRow++][1] = Convert.ToString(TimeSpan.Parse("0." + data.ExperimentStartTime) - TimeSpan.Parse("0." + data.InitialPreviewTime)) + "";
            stringData[currentRow][0] = "Time from experiment start to first action";
            stringData[currentRow++][1] = Convert.ToString(TimeSpan.Parse("0." + data.SubjectActions[0].Time) - TimeSpan.Parse("0." + data.ExperimentStartTime)) + "";
            stringData[currentRow][0] = "Time from first action to last action";
            stringData[currentRow++][1] = ElapsedTime(data.SubjectActions);
            currentRow++;
            //Move Data
            stringData[currentRow++][0] = "Move Statistics";
            stringData[currentRow][0] = "Total # of moves";
            stringData[currentRow++][1] = data.SubjectActions.Length.ToString();
            foreach (StimuliPresentation sp in data.PresentationOrder)
            {
                stringData[currentRow][0] = "Total # of moves for " + sp.Label;
                stringData[currentRow++][1] = Convert.ToString(NumOfMoves(data.SubjectActions, sp));
            }
            currentRow++;
            //Distance Data
            stringData[currentRow++][0] = "Distance Statistics";
            DistanceStat distStat = DistanceAnalysis(data.Distances);
            stringData[currentRow][0] = "Smallest Distance";
            stringData[currentRow++][1] = distStat.min+"";
            stringData[currentRow][0] = "Largest Distance";
            stringData[currentRow++][1] = distStat.max+"";
            stringData[currentRow][0] = "Average Distance";
            stringData[currentRow++][1] = distStat.avg+"";
            StringBuilder sb = new StringBuilder();
            for (int index = 0; index < stringData.Length; index++)  
                sb.AppendLine(string.Join(",", stringData[index]));
            //Save File Dialog
            Microsoft.Win32.SaveFileDialog dlg = new Microsoft.Win32.SaveFileDialog();
            dlg.FileName = "Experiment ID - " + data.ExperimentID + " Analysis"; // Default file name
            dlg.DefaultExt = ".csv"; // Default file extension
            dlg.Filter = "CSV documents (.csv)|*.csv"; // Filter files by extension 

            // Show save file dialog box
            Nullable<bool> result = dlg.ShowDialog();

            // Process save file dialog box results 
            if (result == true)
            {
                // Save document File Writing
                string filename = dlg.FileName;
                File.WriteAllText(filename, sb.ToString());
            }

            return result.Value;

        }
        /// <summary>
        /// This methods takes a file path and a group of SosaData objects
        /// This methods will average the collections of data and then analyze the data using statistical methods
        /// This methods has no returns but will write a csv file to the filepath.
        /// </summary>
        /// <param name="filepath"></param>
        /// <param name="data"></param>
        public static bool CreateGroupAnalysisFile(Group group)
        {
            if (group == null)
                return false;
            if (group.data == null)
                return false;
            if (group.data.Count <= 0)
                return false;
            //Initilize Variables
            int numStimuli = group.data.ElementAt(0).PresentationOrder.Length;
            int numRow = 15 + numStimuli;
            int numCol = 4;
            int currentRow = 0;

            string[][] stringData = new string[numRow][];
            for (int i = 0; i < stringData.Length; i++)
                stringData[i] = new string[numCol];
            GroupAnalysis ga = groupAnalysis(group);
            //Group Id
            stringData[currentRow][0] = "Group Name: ";
            stringData[currentRow++][1] = group.Name;
            stringData[currentRow][0] = "Group Id: ";
            stringData[currentRow++][1] = group.Id;
            currentRow++;
            //Time Data
            stringData[currentRow++][0] = "Average Time Data";
            stringData[currentRow][0] = "Average Time from preview to experiment start";
            stringData[currentRow++][1] = ga.InitialPreviewTime.ToString();
            stringData[currentRow][0] = "Average Time from experiment start to first action";
            stringData[currentRow++][1] = ga.ExperimentStartTime.ToString();
            stringData[currentRow][0] = "Average Time from first action to last action";
            stringData[currentRow++][1] = ga.ElapsedTime.ToString();
            currentRow++;
            //Move Data
            stringData[currentRow++][0] = "Average Move Statistics";
            stringData[currentRow][0] = "Average Total # of moves";
            stringData[currentRow++][1] = ga.averageNumMoves.ToString();
            int c = 0;
            foreach (StimuliPresentation sp in group.data.ElementAt(0).PresentationOrder)
            {
                stringData[currentRow][0] = "Average Total # of moves for " + sp.Label;
                stringData[currentRow++][1] = ga.averageNumLabelMoves[c].ToString();
                c++;
            }
            currentRow++;
            //Distance Data
            stringData[currentRow++][0] = "Distance Statistics";
            stringData[currentRow][0] = "Average Smallest Distance";
            stringData[currentRow++][1] = ga.distanceStat.min+"";
            stringData[currentRow][0] = "Average Largest Distance";
            stringData[currentRow++][1] = ga.distanceStat.max + "";
            stringData[currentRow][0] = "Group Average Distance";
            stringData[currentRow++][1] = ga.distanceStat.avg + "";
            StringBuilder sb = new StringBuilder();
            for (int index = 0; index < stringData.Length; index++)
                sb.AppendLine(string.Join(",", stringData[index]));
            //Save File Dialog
            Microsoft.Win32.SaveFileDialog dlg = new Microsoft.Win32.SaveFileDialog();
            dlg.FileName = "Group Name - " + group.Name + " Group Id - " + group.Id + " " + " Analysis"; // Default file name
            dlg.DefaultExt = ".csv"; // Default file extension
            dlg.Filter = "CSV documents (.csv)|*.csv"; // Filter files by extension 

            // Show save file dialog box
            Nullable<bool> result = dlg.ShowDialog();

            // Process save file dialog box results 
            if (result == true)
            {
                // Save document File Writing
                string filename = dlg.FileName;
                File.WriteAllText(filename, sb.ToString());
            }

            return result.Value;
        }

        #region Single File Analysis

        /// <summary>
        /// This methods takes an array of the 'Experimental Subject's' moves and a single stimuli
        /// This methods iterates through the subjectactions and finds how many times the stimuli was moved
        /// This methods returns the number of times a specific stimuli was moved.
        /// </summary>
        /// <param name="sa"></param>
        /// <param name="sp"></param>
        /// <returns></returns>
        protected static int NumOfMoves(SubjectAction[] sa,StimuliPresentation sp)
        {
            int count = 0;
            for (int i = 0; i < sa.Length; i++)
            {
                if (sa[i].Label == sp.Label)
                    count++;
            }
            return count;
        }
        /// <summary>
        /// This methods takes an array of SubjectActions
        /// This method calculates the difference from the last move to the first move in time
        /// This methods returns the difference in time.
        /// </summary>
        /// <param name="subjectActions"></param>
        /// <returns></returns>
        protected static string ElapsedTime(SubjectAction[] subjectActions)
        {
            TimeSpan iTime = TimeSpan.Parse("0." + subjectActions[0].Time);
            TimeSpan fTime = TimeSpan.Parse("0." + subjectActions[subjectActions.Length - 1].Time);
            return fTime.Subtract(iTime).ToString();
        }

        
        /// <summary>
        /// This methods takes a 2D Matrix of relative distances of stimuli.
        /// This methods find the smallest, largest and average distance.
        /// This methods returns the smallest, largest and average distance.
        /// </summary>
        /// <param name="distances"></param>
        /// <returns></returns>
        protected static DistanceStat DistanceAnalysis(double[][]  distances)
        {
            double min = double.MaxValue;
            double max = double.MinValue;
            double sum = 0;
            double count = 0;

            for (int i = 1; i < distances.Length; i++)
            {
                for (int j = 0; j < i; j++)
                {
                    double temp = distances[i][j];
                    if (min > temp) min = temp;
                    if (max < temp) max = temp;
                    sum += temp;
                    count++;
                }
            }

            return new DistanceStat(min, max, sum / count);
        }

        #endregion Single File Analysis

        #region Group File Analysis

        /// <summary>
        /// This method will calculate the analysis points of a Group
        /// Paramter: Group
        /// Return: GroupAnalysis
        /// </summary>
        /// <param name="group"></param>
        /// <returns></returns>
        protected static GroupAnalysis groupAnalysis(Group group)
        {
            if (group == null)
                return null;

            //Initilize accumulator variables
            GroupAnalysis ga = new GroupAnalysis();
            long experimentStartTime = 0;
            long initialPreviewTime = 0;
            long elapsedTime = 0;
            int count = group.data.Count;
            int numberOfMoves = 0;
            int[] numberOfLabelMoves = new int[group.data.ElementAt(0).PresentationOrder.Length];
            for (int i = 0; i < numberOfLabelMoves.Length; i++)
                numberOfLabelMoves[i] = 0;
            DistanceStat ds = new DistanceStat(0,0,0);

            //Add values to accumulators
            foreach(SosaData data in group.data)
            {
                experimentStartTime += (TimeSpan.Parse("0." + data.ExperimentStartTime) - TimeSpan.Parse("0." + data.InitialPreviewTime)).Ticks;
                initialPreviewTime += (TimeSpan.Parse("0." + data.SubjectActions[0].Time) - TimeSpan.Parse("0." + data.ExperimentStartTime)).Ticks;
                elapsedTime += ElapsedTimeSpan(data.SubjectActions).Ticks;
                numberOfMoves += data.SubjectActions.Length;
                for (int i = 0; i < numberOfLabelMoves.Length; i++)
                {
                    numberOfLabelMoves[i] += NumOfMoves(data.SubjectActions, data.PresentationOrder.ElementAt(i));
                }
                ds.add(DistanceAnalysis(data.Distances));
            }

            //Divide accumulators by n to find average
            ga.ExperimentStartTime = new TimeSpan(experimentStartTime / count);
            ga.InitialPreviewTime = new TimeSpan(initialPreviewTime / count);
            ga.ElapsedTime = new TimeSpan(elapsedTime / count);
            ga.averageNumMoves = (numberOfMoves / count);
            ga.averageNumLabelMoves = new int[numberOfLabelMoves.Length];
            for (int i = 0; i < numberOfLabelMoves.Length; i++)
                ga.averageNumLabelMoves[i] = (numberOfLabelMoves[i] / count);
            ga.distanceStat = ds.divide(count);

            return ga;
        }

        /// <summary>
        /// Returns a TimeSpan of the first move to the last move
        /// </summary>
        /// <param name="subjectActions"></param>
        /// <returns></returns>
        protected static TimeSpan ElapsedTimeSpan(SubjectAction[] subjectActions)
        {
            TimeSpan iTime = TimeSpan.Parse("0." + subjectActions[0].Time);
            TimeSpan fTime = TimeSpan.Parse("0." + subjectActions[subjectActions.Length - 1].Time);
            return fTime.Subtract(iTime);
        }

        #endregion Group File Analysis

    }
}
